package at.jku.ase.server;

import at.jku.ase.communication.SessionObject;
import at.jku.ase.communication.parsers.*;
import at.jku.ase.utils.CryptUtils;
import at.jku.ase.utils.OpThrowsException;
import at.jku.ase.utils.XMLUtils;

import javax.xml.stream.*;
import java.io.IOException;
import java.net.Socket;

public class ServerThread extends Thread {
    private Socket client;
    private XMLInputFactory inputFactory = XMLInputFactory.newInstance();
    private XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
    private XMLStreamReader reader;
    private XMLStreamWriter writer;
    private StanzaParser parser;
    private SessionObject session;

    public ServerThread(Socket client) {
        System.out.println("Init server thread...");
        this.client = client;
        init();
    }

    private void init() {
        try {
            this.reader = inputFactory.createXMLStreamReader(client.getInputStream());
            this.writer = outputFactory.createXMLStreamWriter(client.getOutputStream());
        } catch (XMLStreamException e) {
            throw new RuntimeException("Error when creating input factory.", e);
        } catch (IOException e) {
            throw new RuntimeException("Cannot get the input stream from socket", e);
        }
        System.out.println("Init session object...");
        this.session = SessionObject.newInstance();
        System.out.println("Init xml stream parser...");
        parser = new StanzaParser();
        parser.registerParser("publicKey", new PublicKeyParser(session, new XMLResponse() {
            public void response() throws IOException, XMLStreamException {
                XMLUtils.startXMLStream(writer);
                XMLUtils.writePublicKey(writer, session);
                writer.flush();
            }
        }));
        parser.registerParser("secretKey", new SecretKeyParser(session, new XMLResponse() {
            public void response() throws IOException, XMLStreamException {
                XMLUtils.writeEncryptedSecretKey(writer, CryptUtils.encryptSecretKey(session.getReceiverPublicKey(), session.getSecretKey()));
                writer.flush();
            }
        }));
        parser.registerParser("transmission", new TransmissionParser(session, new XMLResponse() {
            public void response() throws IOException, XMLStreamException {
                System.out.println("Signature Status: " + session.getSignature());
                System.out.println("Sending feedback to client...");
                XMLUtils.writeSignatureStatus(writer, session.isSignValid());
                XMLUtils.endXMLStream(writer);
                writer.flush();
            }
        }));
    }

    public void run() {
        System.out.println("Server run...");
        parseXMLStream();
//        dumpXMLStream();
    }

    private void dumpXMLStream() {
        byte[] bytes = new byte[2048];
        try {
            while (client.getInputStream().read(bytes) != -1) {
                System.out.print(new String(bytes));
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private void parseXMLStream() {
        new OpThrowsException() {
            @Override
            public void op() throws Exception {
                parser.parse(reader);
                System.out.println("Closing Current Server Thread...");
            }
            public void cleanUp() throws Exception {
                XMLUtils.endXMLStream(writer);
                ServerThread.this.client.close();
            }
        }.execute();

    }

}